/// Created by Nazariy Jaworski Copyright (c) 2013
/// Bot program
///
/// botcommunicator.cpp -
///  implements communication between bots on
///  different machines
///
/// To Do list:
///  - ...
///
/// v 0.1        08.04.2013

#include "bot.h"
#include "botcommunicator.h"
#include <QtNetwork/QNetworkInterface>
#include <QTimer>

// Extra local defenitions
#define PARAM_NO_ANSWER     0
#define PARAM_IS_ANSWER     1

// Extra local objects
int step = 0;
QTimer *myTimer;
bool isWaitingFor_REQUEST = false;

BotCommunicator::BotCommunicator(QObject *parent): QObject(parent)
// Common constructor
{
    // keep it clean, use 'Init()' for extra initializations
}
void  BotCommunicator::Init()
{
    myState             = IsNotRegistered;

    // Initialization of his main objects
    myQTcpSocket        = new QTcpSocket(this);
    myQUdpInputSocket   = new QUdpSocket(this);
    myQUdpOutputSocket  = new QUdpSocket(this);

    myTimer = new QTimer();

    // Binding
    connect(myQTcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(OnTcpSocketError(QAbstractSocket::SocketError)));
    connect(myQTcpSocket,SIGNAL(readyRead()),
            this, SLOT(OnTcpSocketInput()));
    connect(myQTcpSocket,SIGNAL(disconnected()),
            this, SLOT(OnTcpSocketDisconnected()));

    connect(myQUdpInputSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(OnUdpInputSocketError(QAbstractSocket::SocketError)));
    connect(myQUdpOutputSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(OnUdpOutputSocketError(QAbstractSocket::SocketError)));
    connect(myQUdpInputSocket, SIGNAL(readyRead()), this, SLOT(OnUdpSocketInput()));
    if(!myQUdpInputSocket->bind(DEFAULT_UDP_PORT, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint))
        Bot::GetInstance()->SLOT_FatalError("FATAL ERROR: Can't bind to UDP input socket\n");
}

void BotCommunicator::RunBotServer()
{
    // Server shoul be only one!
    myServer = new BotServer(this);
}

void BotCommunicator::RegisterAtEnvironment(int parameter)
{
    if(myState == IsNotRegistered)
    {
        myTimer->stop();    // Stop sending 'MESSAGE_REQUEST'
        disconnect(myTimer, SIGNAL(timeout()), this, SLOT(RegisterAtEnvironment()));
        switch(parameter)
        {
            case PARAM_NO_ANSWER:
            {
                isWaitingFor_REQUEST = true;
                if(step < DEFAULT_REQUEST_MESSAGE_COUNT)
                {
                    // Sending message 'MESSAGE_REQUEST' to another bots
                    myQUdpOutputSocket->writeDatagram(MESSAGE_REQUEST,
                        QHostAddress::Broadcast, DEFAULT_UDP_PORT);
                    Bot::GetInstance()->SIGNAL_WriteToLog(
                                QString(UI_STR_MESSAGE_UDP_OUTPUT) + MESSAGE_REQUEST + "\n");

                    step++;
                    connect(myTimer, SIGNAL(timeout()), this, SLOT(RegisterAtEnvironment()));
                    myTimer->start(DEFAULT_REQUEST_MESSAGE_WAIT);
                }
                else //still no answer - run BotServer mode
                {
                    //step = 0;
                    //isWaitingFor_REQUEST = false;
                    RunBotServer();

                    /// \todo If server runs at separate process,
                    // remove those socket connections, and recall this method
                    /*QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
                        // use the first non-localhost IPv4 address
                        for (int i = 0; i < ipAddressesList.size(); i++)
                        {
                            if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
                                ipAddressesList.at(i).toIPv4Address())
                            {
                                myQTcpSocket->connectToHost(ipAddressesList.at(i), DEFAULT_TCP_SERVER_PORT);
                                myQTcpSocket->waitForConnected();
                                break;
                            }
                        }
                    myState = IsRegistered;*/
                    step = 0;
                    connect(myTimer, SIGNAL(timeout()), this, SLOT(RegisterAtEnvironment()));
                    myTimer->start(DEFAULT_REQUEST_MESSAGE_WAIT);

                    Bot::GetInstance()->SIGNAL_WriteToLog(" Still no answer, bot now owns the Server\n");
                    // done!
                }
                break;
            }
            case PARAM_IS_ANSWER:
            {
                step = 0;
                isWaitingFor_REQUEST = false;
                Bot::GetInstance()->SIGNAL_WriteToLog(" Have got an answer, Bot become the Client\n");
                // done!
            }
        }
    }
    else
    {
        Bot::GetInstance()->SIGNAL_WriteToLog(" Have already been registered\n");
        // done!
    }
}

void BotCommunicator::OnTcpSocketError(QAbstractSocket::SocketError socketError)
{
    /// \todo
    Bot::GetInstance()->SIGNAL_WriteToLog(" Some error\n");
}

void BotCommunicator::OnUdpInputSocketError(QAbstractSocket::SocketError socketError)
{
    /// \todo
    Bot::GetInstance()->SIGNAL_WriteToLog(" Some error\n");
}

void BotCommunicator::OnUdpOutputSocketError(QAbstractSocket::SocketError socketError)
{
    /// \todo
    Bot::GetInstance()->SIGNAL_WriteToLog(" Some error\n");
}

void BotCommunicator::OnTcpSocketDisconnected()
{
    myState = IsNotRegistered;
    Bot::GetInstance()->SIGNAL_WriteToLog(" Connection with server was lost\n");
}

void BotCommunicator::WriteToTcpSocket()
{
    if(myState == IsRegistered)
    {
        /// \todo
    }
    else
        Bot::GetInstance()->SIGNAL_WriteUIString(" Error!: Bot is not registered at environment\n");
}

void BotCommunicator::ShowClients() const
{
    if(myServer)
    {
        Bot::GetInstance()->SIGNAL_WriteUIString(myServer->ShowClients());
    }
    else
        Bot::GetInstance()->SIGNAL_WriteUIString(" Error!: Bot is not in the Server mode\n");
}

void BotCommunicator::OnTcpSocketInput()
// Processing TCP input data
{
    /// \todo
}

void BotCommunicator::OnUdpSocketInput()
// Processing UDP input data
{
    while(myQUdpInputSocket->hasPendingDatagrams())
    {
        // Get message
        QByteArray datagram;
        datagram.resize(myQUdpInputSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        myQUdpInputSocket->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);
        QString str = QString(UI_STR_MESSAGE_UDP_INPUT) + " From " + sender.toString() + ":" +
            QString::number(senderPort) + " message '" + datagram.data() + "'\n";
        Bot::GetInstance()->SIGNAL_WriteToLog(str);

        // Process message
        if(myState == IsNotRegistered && isWaitingFor_REQUEST && !QString::compare(datagram.data(),MESSAGE_ACCEPT))
            // Bot was serching for server, and he has found it now
        {
            RegisterAtEnvironment(PARAM_IS_ANSWER); // Turn off sending 'MESSAGE_REQUEST'
            // Make TCP connection
            myQTcpSocket->connectToHost(sender, DEFAULT_TCP_SERVER_PORT);
            myQTcpSocket->waitForConnected();
            myState = IsRegistered;
            Bot::GetInstance()->SIGNAL_WriteToLog(" Bot has found the Server - " + sender.toString() + ":" + QString::number(senderPort) + "\n");
        }
    }
}

BotCommunicator::~BotCommunicator()
// Common destructor
{
    // Destruction of his main objects
    if(myServer)
        delete myServer;
    if(myQTcpSocket)
        delete myQTcpSocket;
    if(myQUdpInputSocket)
        delete myQUdpInputSocket;
    if(myQUdpOutputSocket)
        delete myQUdpOutputSocket;

    delete myTimer;
}
